/* Project3: filehdr.h

	Modifications by: Cary Rivet
	Modified:	Allocate(BitMap *freeMap, int fileSize)
				Deallocate(BitMap *freeMap)
				ByteToSector(int offset)
				Print()
*/



// filehdr.cc 
//	Routines for managing the disk file header (in UNIX, this
//	would be called the i-node).
//
//	The file header is used to locate where on disk the 
//	file's data is stored.  We implement this as a fixed size
//	table of pointers -- each entry in the table points to the 
//	disk sector containing that portion of the file data
//	(in other words, there are no indirect or doubly indirect 
//	blocks). The table size is chosen so that the file header
//	will be just big enough to fit in one disk sector, 
//
//      Unlike in a real system, we do not keep track of file permissions, 
//	ownership, last modification date, etc., in the file header. 
//
//	A file header can be initialized in two ways:
//	   for a new file, by modifying the in-memory data structure
//	     to point to the newly allocated data blocks
//	   for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "system.h"
#include "filehdr.h"

//----------------------------------------------------------------------
// FileHeader::Allocate
// 	Initialize a fresh file header for a newly created file.
//	Allocate data blocks for the file out of the map of free disk blocks.
//	Return FALSE if there are not enough free blocks to accomodate
//	the new file.
//
//	"freeMap" is the bit map of free disk sectors
//	"fileSize" is the bit map of free disk sectors
//----------------------------------------------------------------------

bool
FileHeader::Allocate(BitMap *freeMap, int fileSize)
{ 

	// Begin: Modifications by Cary Rivet for Project03

	/*	added printfs to show that file header and content are being properly allocated
		in the fileSystem
		
		determines which allocation method to use depending on the number of Bytes
		the file contains

	*/
//	printf("\nAllocating space for file header\n");
	
	// printing out bitmap before allocation
//	printf("FileSys before allocation of file:\n\n");
//	for(int i = 0; i < NumSectors; i++){
//		if(freeMap->Test(i) == true)
//			printf("%d",1);
//		else
//			printf("%d",0);
//	}
//	printf("\n\n");

	    
	numBytes = fileSize;
    numSectors  = divRoundUp(fileSize, SectorSize);
	
//	printf("numBytes in file %d\n",numBytes);
//	printf("numSectors to allocate %d\n",numSectors);
	
	// ensure enough room oclearn disk
    if (freeMap->NumClear() < numSectors){
		printf("not enough space in bitmap\n\n");
		return FALSE;		// not enough space
	}
	// need to add if else to place an indirect pointer to another fileheader
	// depending on how large the file is and choose to allocate the correct
	
	if(numBytes > type3_minSize){ // if size is > 32000 bytes (type 3)
//		printf("Adding file of size %d (type 3).\n",numBytes);
		// allocate normally and when get to the 2nd to last ptr create list of sectors 
		// and start adding to its sectors, once finished, create a pointer 
		// to another list of pointers that point to direct pointers.
		
		// int to keep track of sectors to write
		int countDownSectors = numSectors;
		
		// allocate available bytes in the bitmap for the file in first avail
		//	spots 
		for (int i = 0; i < (int)NumDirect; i++ , countDownSectors--){
			dataSectors[i] = freeMap->Find();		
		}
		
		// increment to account for 2 indirect pointers
		countDownSectors++;
		countDownSectors++;
		
		
		int indirectPtr[NumIndirect];
		int doublePtr[NumIndirect];
		
		// continue allocation for the indirect ptrs		
		for(int i = 0; i < (int)NumIndirect; i++, countDownSectors--)
			indirectPtr[i] = freeMap->Find();		
		
		// place the indirect pointer in the sector table
		synchDisk->WriteSector(dataSectors[NumDirect-2], (char *)indirectPtr);
		
		
		// populate doublely pointed pointer to point to more pointers
		for(int i = 0; i < (int)NumIndirect; i++){			
			
			// if sectors left to account for
			if(countDownSectors > 0){
				
				// allocate a sector for this pointer
				doublePtr[i] = freeMap->Find();
				
				
				// declare a new array for the direct pointers
				int doublesDirects[NumIndirect];
			
				// populate indirect pointer's direct pointers 
				//		here I need to worry about number of sectors left to write
				for(int j = 0; j < (int)NumIndirect ; j++, countDownSectors--){
					
					// if file sectors left to write add to bitmap
					if(countDownSectors > 0){
						doublesDirects[j] = freeMap->Find();
					}else{	// otherwise mark sector as -1
						doublesDirects[j] = -1;
					}								
				}
				
				// add this set of Direct ptrs to the disk
				synchDisk->WriteSector(doublePtr[i], (char *)doublesDirects);				 
				
			}else{
				// mark indirect as invalid
				doublePtr[i] = -1;
			}
			
			
		}
		
		
		// write double indirect to disk
		synchDisk->WriteSector(dataSectors[NumDirect-1], (char *)doublePtr);
				
		
		
	}else if(numBytes <= type3_minSize && numBytes > type2_minSize){	// if  15872 < size <= 32000 bytes (type 2)
//		printf("Adding file of size %d (type 2).\n",numBytes);
		// allocate available bytes in the bitmap for the file in first avail
		//	spots 
		
		// int to keep track of sectors to write
		int countDownSectors = numSectors;
		
		// allocate sector for each pointer
		for (int i = 0; i < (int)NumDirect; i++, countDownSectors--)
			dataSectors[i] = freeMap->Find();		
		
		// increment to account for indirect pointer
		countDownSectors++;
		
		
		int indirectPtr[NumIndirect];
		
		// continue allocation for the indirect ptr's addresses		
		for(int i = 0; i < NumIndirect; i++,countDownSectors--){
			if(countDownSectors > 0)
				indirectPtr[i] = freeMap->Find();		
			else
				indirectPtr[i] = -1;	
		}
		
		
		// place the indirect pointer in the sector table
		synchDisk->WriteSector(dataSectors[NumDirect-1], (char *)indirectPtr);
		
		
		
				
	}else{ // if size <= 15872 (type 1 default)
	
	
//		printf("Adding file of size %d (type 1).\n",numBytes);
		
		// int to keep track of sectors to write
		int countDownSectors = numSectors;
				
		// allocate available bytes in the bitmap for the file in first avail
		//	spots 
		for (int i = 0; i < (int)NumDirect; i++, countDownSectors--){
		
			if(countDownSectors > 0)
				dataSectors[i] = freeMap->Find();
			else
				dataSectors[i] = -1;		
		}
		
	}
	

	
	
//	printf("File's header allocated in file system.\n");
	
	
	// printing out bitmap after allocation
//	printf("FileSys after allocation of file:\n\n");
//	for(int i = 0; i < NumSectors; i++){
//		if(freeMap->Test(i) == true)
//			printf("%d",1);
//		else
//			printf("%d",0);
//	}
//	printf("\n\n");
	
	
	
	
	// end: Modifications by Cary Rivet for Project03

	// if successful allocation return true
    return TRUE;
}

//----------------------------------------------------------------------
// FileHeader::Deallocate
// 	De-allocate all the space allocated for data blocks for this file.
//
//	"freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------

void 
FileHeader::Deallocate(BitMap *freeMap)
{
	
	// Begin: Modifications by Cary Rivet for Project03

	/*	added the functionality to remove ALL of the sectors allocated bytes allocated
		by allocate depending on the number of Bytes the file takes up.
		
	*/
	
	if(numBytes > type3_minSize){ // if size is > 32000 bytes (type 3)

		
		// deallocate allocated direct pointers
		for (int i = 0; i < (int)NumDirect; i++){
			ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
			freeMap->Clear((int) dataSectors[i]);
		}	
		
		// deallocate indirect pointers
		int indirects[NumIndirect];
		synchDisk->ReadSector(dataSectors[NumDirect-2], (char *)indirects);
		
		for(int i = 0; i < NumIndirect;i++){
			
			if(indirects[i] != -1){
				ASSERT(freeMap->Test((int) indirects[i]));  // ought to be marked!
				freeMap->Clear((int) indirects[i]);
			}
			
			
		}
		
		// Deallocate the doubly indirect pointers
		int doubleIndirects[NumIndirect];
		synchDisk->ReadSector(dataSectors[NumDirect-1], (char *)doubleIndirects);
		
		// deallocate the doubly indirect pointers themselves
		for(int i = 0; i < NumIndirect;i++){
			
			if(doubleIndirects[i] != -1){
				ASSERT(freeMap->Test((int) doubleIndirects[i]));  // ought to be marked!
				freeMap->Clear((int) doubleIndirects[i]);
			}
			
			
		}
		
		
		// deallocate the indirect pointers
		for(int i = 0; i < NumIndirect; i++){
			
			if(doubleIndirects[i] != -1){
				
				int Indirects2[NumIndirect];
				synchDisk->ReadSector(doubleIndirects[i], (char *)Indirects2);
		
				
				for(int j = 0; j < NumIndirect; j++){
					
					if(Indirects2[j] != -1){
						ASSERT(freeMap->Test((int) Indirects2[j]));  // ought to be marked!
						freeMap->Clear((int) Indirects2[j]);
						
						
					}
					
					
				}				
				
				
			}
			
			
		}
		
		
		
		
				
		
	}else if(numBytes <= type3_minSize && numBytes > type2_minSize){	// if  15872 < size <= 32000 bytes (type 2)
		
		
		// deallocate allocated direct pointers
		for (int i = 0; i < (int)NumDirect; i++){
			ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
			freeMap->Clear((int) dataSectors[i]);
		}	
		
		
		// deallocate indirect pointers
		int indirects[NumIndirect];
		synchDisk->ReadSector(dataSectors[NumDirect-1], (char *)indirects);
		
		for(int i = 0; i < NumIndirect;i++){
			
			if(indirects[i] != -1){
				ASSERT(freeMap->Test((int) indirects[i]));  // ought to be marked!
				freeMap->Clear((int) indirects[i]);
			}
			
			
		}	
		
		
	}else{ // if size <= 15872 (type 1 default)
	
		// remove allocated bytes for the bitmap
		for (int i = 0; i < numSectors; i++) {
			ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
			freeMap->Clear((int) dataSectors[i]);
		}	
	
	}
	
	
	// printing out bitmap after allocation
//	printf("FileSys after deallocation of file contents\n");
//	printf("(*NOTE* only content is removed within deallocate,\n");
//	printf("Extra sector for the file header IS removed after this):\n\n");
//	for(int i = 0; i < NumSectors; i++){
//		if(freeMap->Test(i) == true)
//			printf("%d",1);
//		else
//			printf("%d",0);
//	}
//	printf("\n\n");
	
	// end: Modifications by Cary Rivet for Project03

	
}

//----------------------------------------------------------------------
// FileHeader::FetchFrom
// 	Fetch contents of file header from disk. 
//
//	"sector" is the disk sector containing the file header
//----------------------------------------------------------------------

void
FileHeader::FetchFrom(int sector)
{
    synchDisk->ReadSector(sector, (char *)this);

}

//----------------------------------------------------------------------
// FileHeader::WriteBack
// 	Write the modified contents of the file header back to disk. 
//
//	"sector" is the disk sector to contain the file header
//----------------------------------------------------------------------

void
FileHeader::WriteBack(int sector)
{
    synchDisk->WriteSector(sector, (char *)this); 

}

//----------------------------------------------------------------------
// FileHeader::ByteToSector
// 	Return which disk sector is storing a particular byte within the file.
//      This is essentially a translation from a virtual address (the
//	offset in the file) to a physical address (the sector where the
//	data at the offset is stored).
//
//	"offset" is the location within the file of the byte in question
//----------------------------------------------------------------------

int
FileHeader::ByteToSector(int offset)
{

	// Begin: Modifications by Cary Rivet for Project03
	
	/*
		Added the ability to calculate and retrieve the correct byte from the disk
		depending on the allocation type, accounting for indirect and doubly indirect
		pointers.	
	*/
	
	
	//"offset" is the location within the file of the byte in question
	
	int currentSector = offset/SectorSize;
		
	// edit to account for different types
	if(numBytes > type3_minSize){ // if size is > 32000 bytes (type 3)
			
		
		if(currentSector < (int)NumDirect-2){

			// if the direct pointers
			return(dataSectors[currentSector]);
						
		}else if(currentSector >= (int)NumDirect-2 && currentSector < (int)NumDirect-2+NumIndirect){  
			// if current sector is in the indirect pointers
			
			
			// read sector in dataSectors[NumDirect-1] and use the array in it to get the correct sector
			int indirects[NumIndirect];		
			synchDisk->ReadSector(dataSectors[NumDirect-2], (char*)indirects);
			
			//for(int i = 0; i < NumIndirect; i++)
			//	printf("Indirects ptr = %d\n", indirects[i]);
			
			
			return( indirects[currentSector-NumDirect+2] );
			
			
		}else{
		
			//printf("Doubly Indirects\n");

			// get the list of indirect pointers
			//printf("currentSector = %d\n", currentSector);
			
			int indirects[NumIndirect];		
			synchDisk->ReadSector(dataSectors[NumDirect-1], (char*)indirects);
			
			// look at the correct indirect ptr depending on currentSector
			
			
			// if 124 look in 0 and get 0
			// if 125 look in 0 and get 1
			//...
			// if 188 look in 1 and get 0
			// ...
			// if 252 look in 2 and get 0
			
			// get the sector relative to the double indirect pointers
			int relativeSector = currentSector-(NumDirect-2)-NumIndirect;
			
			//printf("relativeSector (preCalc) = %d\n", relativeSector);
			
			
			// counter to figure out correct indirect pointer to look in
			int currentIndirect;
			
			// this loop will subtract from relative sector until it points to the correct
			// sector relative to the current Indirect pointer being looked at
			// if not 0<=x < 64 then move on to the next indirect pointer
			for(currentIndirect = 0; currentIndirect < NumIndirect; currentIndirect++){
				if(relativeSector > NumIndirect)
					relativeSector -= NumIndirect;
				else
					break;
			}
			
			//printf("relativeSector (postCalc) = %d\n", relativeSector);
			//printf("currentIndirect = %d\n", currentIndirect);
			
			
			
			// now currentIndirect will be the appropriate Indirect pointer
			// and relativeSector will be the appropriate sector relative to 
			// the page that contains the currentSector(the passed one)
			
			int directs[NumIndirect];
			
			// read direct pointers from the appropriate indirect table 
			synchDisk->ReadSector(indirects[currentIndirect], (char*)directs);
			
			
			
			
			// return the correct direct pointer relative sector from the indirect table
			return(directs[relativeSector]);
			
		}
		
		
		
		
		
		
		
		
	}else if(numBytes <= type3_minSize && numBytes > type2_minSize){	// if  15872 < size <= 32000 bytes (type 2)
		
		
		if(currentSector < (int)NumDirect-1){
			// if the direct pointers
			return(dataSectors[currentSector]);
						
		}else{  
			// if current sector is in the indirect pointers
			
			// read sector in dataSectors[NumDirect-1] and use the array in it to get the correct sector
			int indirects[NumIndirect];		
			
			synchDisk->ReadSector(dataSectors[NumDirect-1], (char*)indirects);
			
			return( indirects[currentSector-NumDirect+1] );
			
			
		}
		
		
	}else{ // if size <= 15872 (type 1 default)
		
		return(dataSectors[currentSector]);
		
	}
	
		// End: Modifications by Cary Rivet for Project03

    
}

//----------------------------------------------------------------------
// FileHeader::FileLength
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int
FileHeader::FileLength()
{
    return numBytes;
}

//----------------------------------------------------------------------
// FileHeader::Print
// 	Print the contents of the file header, and the contents of all
//	the data blocks pointed to by the file header.
//----------------------------------------------------------------------

void
FileHeader::Print()
{
	// Begin: Modifications by Cary Rivet for Project03
	
	/*
		added the ability to print from the correct sector's contents depending on the 
		allocation type, accounting for indirect and doubly indirect pointers		
		
	*/
	
	if(numBytes > type3_minSize){ // if size is > 32000 bytes (type 3)
		
		
		int i, j, k;
		char *data = new char[SectorSize];

		printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
		
		// NEEDS TO CHANGE
		//for (i = 0; i < numSectors; i++)
		//	printf("%d ", dataSectors[i]);
		
		// print the direct pointers contents
		for(int m = 0; m < (int) NumDirect - 2; m++){
			printf("%d ", dataSectors[m]);
		}
		
		//*******************************INDIRECTS
		// read sector in dataSectors[NumDirect-2] and use the array in it to get the correct sector
		
		int indirects[NumIndirect];
		
		synchDisk->ReadSector(dataSectors[NumDirect-2], (char*)indirects);
		
		
		
		// print indirect pointers allocated sectors
		for(int m = 0; m < (int) NumIndirect; m++){
			if(indirects[m] != -1)
				printf("%d ", indirects[m]);
			
		}		
		
				
		//*********************************DOUBLE DIRECTS
		
		// read in the indirects stored in 
		int doubleIndirects[NumIndirect];
		int Indirects2[NumIndirect];
		synchDisk->ReadSector(dataSectors[NumDirect-1], (char*)doubleIndirects);
		
		
		// printout the contents of the indirects
		for(int m = 0;m < NumIndirect; m++){
			// read in the current indirect pointer to work with
			if(doubleIndirects[m] != -1){
				synchDisk->ReadSector(doubleIndirects[m], (char*)Indirects2);
						
				// FIGURE OUT THE WEIRD OUTPUT FROM HERE!!!!! 
				
				
				// printout the contents of the indirects
				for(int o = 0; o < NumIndirect; o++){
					if(Indirects2[o] != -1)
						printf("%d ", Indirects2[o]);				
				}
			}else{
				break;
			}
						
		}		
		
		//************************************PRINTING CONTENTS
		
		
		
		printf("\nFile contents:\n");
		
		//NEEDS TO CHANGE
		
		// printing direct pointers contents
		for (i = k = 0; i < (int)NumDirect-1 ; i++) {		
			
			synchDisk->ReadSector(dataSectors[i], data);
			
			for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
				if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
					printf("%c", data[j]);
				else
					printf("\\%x", (unsigned char)data[j]);
				
			}
			printf("\n"); 
		}
		
		
		// printing indirect pointer's sectors contents
		for(i = k = 0; i < NumIndirect ; i++){
			
			//printf("\n\nprinting from sector : %d\n", ((fileSectors*)dataSectors[NumDirect-1])->sectors[i]);
			
			
			// if the pointer points to a sector
			if(indirects[i] != -1){
			
				synchDisk->ReadSector( indirects[i] , data);
				
				for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
					if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
						printf("%c", data[j]);
					else
						printf("\\%x", (unsigned char)data[j]);
						
				}
				printf("\n");
				
			}
		}
		
		
		// printing doubly indirect pointer's sectors contents
		for(i = 0; i < NumIndirect ; i++){
			
			if(doubleIndirects[i] != -1){
				
				synchDisk->ReadSector(doubleIndirects[i], (char*)Indirects2);
				
				for(int m = 0, k = 0; m < NumIndirect; m++){
					// if the pointer points to a sector
					if(Indirects2[m] != -1){
					
						synchDisk->ReadSector( Indirects2[m] , data);
						
						for (j = 0 ; (j < SectorSize) && (k < numBytes); j++, k++) {
							if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
								printf("%c", data[j]);
							else
								printf("\\%x", (unsigned char)data[j]);
								
						}
						printf("\n");
						
					}
				}
				
			}
		}
		
		
		
		
		
		
		
		
		delete [] data;
				
		
	}else if(numBytes <= type3_minSize && numBytes > type2_minSize){	// if  15872 < size <= 32000 bytes (type 2)
	
		int i, j, k;
		char *data = new char[SectorSize];

		printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
		
		// NEEDS TO CHANGE
		//for (i = 0; i < numSectors; i++)
		//	printf("%d ", dataSectors[i]);
		
		// print the direct pointers contents
		for(int m = 0; m < (int) NumDirect - 1; m++){
			printf("%d ", dataSectors[m]);
		}
		
		// read sector in dataSectors[NumDirect-1] and use the array in it to get the correct sector
		
		int indirects[NumIndirect];
		
		synchDisk->ReadSector(dataSectors[NumDirect-1], (char*)indirects);
		
		
		
		// print indirect pointers allocated sectors
		for(int m = 0; m < (int) NumIndirect; m++){
			if(indirects[m] != -1)
				printf("%d ", indirects[m]);
			
		}
		
		
		
		
		
		printf("\nFile contents:\n");
		
		//NEEDS TO CHANGE
		
		// printing direct pointers contents
		for (i = k = 0; i < (int)NumDirect-1 ; i++) {		
			
			synchDisk->ReadSector(dataSectors[i], data);
			
			for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
				if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
					printf("%c", data[j]);
				else
					printf("\\%x", (unsigned char)data[j]);
				
			}
			printf("\n"); 
		}
		
		
		// printing indirect pointer's sectors contents
		for(i = k = 0; i < NumIndirect ; i++){
			
			//printf("\n\nprinting from sector : %d\n", ((fileSectors*)dataSectors[NumDirect-1])->sectors[i]);
			
			
			// if the pointer points to a sector
			if(indirects[i] != -1){
			
				synchDisk->ReadSector( indirects[i] , data);
				
				for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
					if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
						printf("%c", data[j]);
					else
						printf("\\%x", (unsigned char)data[j]);
						
				}
				printf("\n");
				
			}
		}	
		
		delete [] data;
		
		
		
		
	}else{ // if size <= 15872 (type 1 default)
		
		int i, j, k;
		char *data = new char[SectorSize];

		printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
		
		for (i = 0; i < numSectors; i++)
			printf("%d ", dataSectors[i]);
		
		
		printf("\nFile contents:\n");
		
		
		for (i = k = 0; i < numSectors; i++) {
			synchDisk->ReadSector(dataSectors[i], data);
			
			for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
				if ('\040' <= data[j] && data[j] <= '\176')   // isprint(data[j])
					printf("%c", data[j]);
				else
					printf("\\%x", (unsigned char)data[j]);
				
			}
			printf("\n"); 
		}
		delete [] data;		
	
	}
		// End: Modifications by Cary Rivet for Project03

}

void FileHeader::Expand(BitMap *myBM, int increaseBy){
	if (myBM->NumClear() < increaseBy){
		printf("not enough space in bitmap\n\n");
		return;		// not enough space
	}
	printf("The Expand Function\n");
	Deallocate(myBM);
	printf("The allocation amount is %d \n", increaseBy);

	Allocate(myBM, increaseBy);
}			
						
void FileHeader::Retract(BitMap *myBM, int decreaseBy){

	Deallocate(myBM);

	Allocate(myBM, numBytes-decreaseBy);

}






